/*
Copyright (C) 2011 The University of Michigan
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Please send inquiries to powertutor@umich.edu
*/
package vn.cybersoft.obs.andriod.batterystats2.components;
import android.content.Context;
import android.hardware.SensorManager;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Set;
import java.util.TreeSet;
import java.util.Map;
import java.util.TreeMap;
import vn.cybersoft.obs.andriod.batterystats2.PowerNotifications;
import vn.cybersoft.obs.andriod.batterystats2.service.IterationData;
import vn.cybersoft.obs.andriod.batterystats2.service.PowerData;
import vn.cybersoft.obs.andriod.batterystats2.util.NotificationService;
import vn.cybersoft.obs.andriod.batterystats2.util.Recycler;
public class Sensors extends PowerComponent {
private final String TAG = "Sensors";
public static final int MAX_SENSORS = 10;
public static class SensorData extends PowerData {
private static Recycler<SensorData> recycler = new Recycler<SensorData>();
public static SensorData obtain() {
SensorData result = recycler.obtain();
if(result != null) return result;
return new SensorData();
}
@Override
public void recycle() {
recycler.recycle(this);
}
public double[] onTime;
private SensorData() {
onTime = new double[MAX_SENSORS];
}
public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
StringBuilder res = new StringBuilder();
for(int i = 0; i < MAX_SENSORS; i++) {
if(onTime[i] > 1e-7) {
res.append("Sensors-time ").append(i).append(" ")
.append(onTime[i]).append("\n");
}
}
out.write(res.toString());
}
}
private Context context;
private SensorManager sensorManager;
private PowerNotifications sensorHook;
private SensorStateKeeper sensorState;
private SparseArray<SensorStateKeeper> uidStates;
public Sensors(Context context) {
this.context = context;
sensorState = new SensorStateKeeper();
uidStates = new SparseArray<SensorStateKeeper>();
if(!NotificationService.available()) {
Log.w(TAG, "Sensor component created although no notification service " +
"available to receive sensor usage information");
return;
}
sensorManager = (SensorManager)context.getSystemService(
Context.SENSOR_SERVICE);
sensorHook = new NotificationService.DefaultReceiver() {
public void noteStartSensor(int uid, int sensor) {
if(sensor < 0 || MAX_SENSORS <= sensor) {
Log.w(TAG, "Received sensor outside of accepted range");
return;
}
synchronized(sensorState) {
sensorState.startSensor(sensor);
SensorStateKeeper uidState = uidStates.get(uid);
if(uidState == null) {
uidState = new SensorStateKeeper();
uidStates.put(uid, uidState);
}
uidState.startSensor(sensor);
}
}
public void noteStopSensor(int uid, int sensor) {
if(sensor < 0 || MAX_SENSORS <= sensor) {
Log.w(TAG, "Received sensor outside of accepted range");
return;
}
synchronized(sensorState) {
sensorState.stopSensor(sensor);
SensorStateKeeper uidState = uidStates.get(uid);
if(uidState == null) {
uidState = new SensorStateKeeper();
uidStates.put(uid, uidState);
}
uidState.stopSensor(sensor);
}
}
};
NotificationService.addHook(sensorHook);
}
@Override
protected void onExit() {
super.onExit();
NotificationService.removeHook(sensorHook);
}
@Override
public IterationData calculateIteration(long iteration) {
IterationData result = IterationData.obtain();
synchronized(sensorState) {
SensorData globalData = SensorData.obtain();
sensorState.setupSensorTimes(globalData.onTime, iterationInterval);
result.setPowerData(globalData);
for(int i = 0; i < uidStates.size(); i++) {
int uid = uidStates.keyAt(i);
SensorStateKeeper uidState = uidStates.valueAt(i);
SensorData uidData = SensorData.obtain();
uidState.setupSensorTimes(uidData.onTime, iterationInterval);
result.addUidPowerData(uid, uidData);
if(uidState.sensorsOn() == 0) {
uidStates.remove(uid);
i--;
}
}
}
return result;
}
private static class SensorStateKeeper {
private int[] nesting;
private long[] times;
private long lastTime;
private int count;
public SensorStateKeeper() {
nesting = new int[MAX_SENSORS];
times = new long[MAX_SENSORS];
lastTime = SystemClock.elapsedRealtime();
}
public void startSensor(int sensor) {
if(nesting[sensor]++ == 0) {
times[sensor] -= SystemClock.elapsedRealtime() - lastTime;
count++;
}
}
public void stopSensor(int sensor) {
if(nesting[sensor] == 0) {
return;
} else if(--nesting[sensor] == 0) {
times[sensor] += SystemClock.elapsedRealtime() - lastTime;
count--;
}
}
public int sensorsOn() {
return count;
}
public void setupSensorTimes(double[] sensorTimes, long iterationInterval) {
long now = SystemClock.elapsedRealtime();
long div = now - lastTime;
if(div <= 0) div = 1;
for(int i = 0; i < MAX_SENSORS; i++) {
sensorTimes[i] = 1.0 * (times[i] +
(nesting[i] > 0 ? now - lastTime : 0)) / div;
times[i] = 0;
}
lastTime = now;
}
}
@Override
public boolean hasUidInformation() {
return true;
}
@Override
public String getComponentName() {
return "Sensors";
}
}